package com.dji.sample.component;

import com.alibaba.fastjson2.JSONObject;
import com.dji.sample.common.error.CommonErrorEnum;
import com.dji.sample.common.model.CustomClaim;
import com.dji.sample.common.model.LoginUser;
import com.dji.sample.common.util.JwtUtil;
import com.dji.sample.manage.model.entity.UserEntity;
import com.dji.sample.manage.service.IUserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Conditional;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@ConditionalOnExpression("'${token.model}'.equals('share')")
@Component
public class ShareAuthInterceptor implements HandlerInterceptor {


    public static final String TOKEN_CLAIM = "customClaim";

    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    @Resource
    RedisTemplate<String, Object> redisTemplate;

    @Resource
    StringRedisTemplate stringRedisTemplate;
    public static final String LOGIN_USER_KEY = "userSign";

    public static final String LOGIN_TOKEN_KEY = "login_tokens:";

    public static final String USER_NAME = "username";

    public static final String TOKEN_PREFIX = "Bearer ";
    @Autowired
    IUserService iUserService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String uri = request.getRequestURI();
        log.debug("request uri: {}", uri);
        // The options method is passed directly.
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }
        String[] tokens = header.split(",");
        String token = null;
        for (String token1 : tokens) {
            if (request.getHeader(token1) != null) {
                token = request.getHeader(token1);
                break;
            }
        }
        // Check if the token exists.
        if (!StringUtils.hasText(token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
        //    log.error(CommonErrorEnum.NO_TOKEN.getErrorMsg());
            return false;
        }
        if (StringUtils.hasText(token) && token.startsWith(TOKEN_PREFIX)) {
            token = token.replace(TOKEN_PREFIX, "");
        }
        // Check if the current token is valid.
        return checkToken(token,response,request);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // Delete the custom data in the request after the request ends.
        request.removeAttribute(TOKEN_CLAIM);
    }

    public boolean checkToken(String token, HttpServletResponse response, HttpServletRequest request) {
        // 解析对应的权限以及用户信息
        Claims claims = null;
        try {
            claims = parseToken(token);
        } catch (Exception e) {
            //可能是飞手端放行
            return privateToken(token, response, request);
        }
        // 解析对应的权限以及用户信息
        String uuid = (String) claims.get(LOGIN_USER_KEY);
        String userKey = getTokenKey(uuid);
        String val = stringRedisTemplate.opsForValue().get(userKey);
        if(val==null){
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        val=val.replace("Set","");
        LoginUser user = JSONObject.parseObject(val,LoginUser.class);
        if (user == null || !token.equals(user.getToken())) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        String userName = (String) claims.get(USER_NAME);
        //查询用户信息
        //先缓存 后查库
        String key = "dj:share:token:user:" + userName;
        CustomClaim customClaim = (CustomClaim) redisTemplate.opsForValue().get(key);
        if (customClaim == null) {
            UserEntity userEntity = iUserService.getUserByUsername(userName);
            if (userEntity == null) {
                throw new RuntimeException("用户不存在");
            }
            customClaim = new CustomClaim(userEntity.getUserId(),
                    userEntity.getUsername(), userEntity.getUserType(),
                    userEntity.getWorkspaceId(), userEntity.getOrgId(), userEntity.getOrgCode());

            long expireTime = user.getExpireTime();
            long currentTime = System.currentTimeMillis();
            redisTemplate.opsForValue().set(key, customClaim, expireTime - currentTime, TimeUnit.MILLISECONDS);
            request.setAttribute(TOKEN_CLAIM, customClaim);
            return true;
        }
        request.setAttribute(TOKEN_CLAIM, customClaim);
        return true;
    }

    private Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    private String getTokenKey(String uuid) {
        return LOGIN_TOKEN_KEY + uuid;
    }

    public boolean privateToken(String token, HttpServletResponse response, HttpServletRequest request) {
        Optional<CustomClaim> customClaimOpt = JwtUtil.parseToken(token);
        if (customClaimOpt.isEmpty()) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        if (!Objects.equals(redisTemplate.opsForValue().get(JwtUtil.getCacheKey(customClaimOpt.get().getId(), customClaimOpt.get().getUserType().toString())), token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }
        // Put the custom data from the token into the request.
        request.setAttribute(TOKEN_CLAIM, customClaimOpt.get());
        return true;

    }
}
